/**
 *  Serponix is an arcade game in focus to multiplayer based on the classic game Snake.
 *  Copyright (C) 2010 - 2011  Daniel Vala
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License,
 *  or  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  If you have any question do not hesitate to contact author
 *  on e-mail address: danielvala42@gmail.com
 */
package com.serponix.game;

import com.serponix.game.objektyNaPlose.Zed;
import com.serponix.net.Informace;
import java.awt.Color;
import java.awt.Graphics;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.swing.JOptionPane;

/**
 * Mapa obsahuje zdi, které mohou být ve hře umístěny.
 * Lze přidat novou zeď nebo ji smazat,
 * poslat informace pro kreslení mapy
 * a uložit ji do souboru či ji ze souboru načíst.
 * Mapu lze také vykreslit s texturou či bez textury.
 *
 * @author Daniel Vala
 */
public class Mapa {

	private static final String PATH;
	private static final String NAME_OF_MAP_FOLDER = "maps";
	private static final String ODDELOVAC1 = "$";
	private static final String ODDELOVAC2 = "|";
	private List<Zed> mapa;
	private String nazev;

	static {
		PATH = Consts.GAME_DIR + NAME_OF_MAP_FOLDER + File.separator;
	}

	/**
	 * Vytvoří prázdnou mapu.
	 */
	public Mapa() {
		mapa = new ArrayList<Zed>();
		nazev = "prázdná mapa";
	}

	/**
	 * Vytvoří mapu dle zadané informace pro kreslení.
	 *
	 * @param m Informace pro kreslení
	 */
	public Mapa(String m) {
		mapa = new ArrayList<Zed>();
		StringTokenizer st = new StringTokenizer(m, ODDELOVAC1);
		while (st.hasMoreTokens()) {
			String souradnice = st.nextToken();
			StringTokenizer st2 = new StringTokenizer(souradnice, ODDELOVAC2);
			mapa.add(new Zed(Integer.parseInt(st2.nextToken()), Integer.parseInt(st2.nextToken())));
		}
	}

	/**
	 * Vrátí cestu k adresáři, kde jsou uložené všechny mapy.
	 * Pokud cesta neexistuje, nejdříve ji vytvoří.
	 *
	 * @return Cesta k adresáři s mapami.
	 */
	public static String getPath() {
		new File(PATH).mkdirs();
		return PATH;
	}

	public void namapujCelouMapu(GameModel model) {
		for (Zed zed : mapa) {
			zed.namapuj(model);
		}
	}

	/**
	 * Vytvoří informace pro kreslení podle všech souřadnic zdí.
	 * Podle tohoto Stringu lze vytvořit novou mapu.
	 *
	 * @return Informace pro kreslení celé mapy.
	 */
	public String getInformaceProKresleni() {
		if (isEmpty()) {
			return "";
		}
		String instrukce = Informace.MAPA + Informace.ODDELOVAC3;
		for (Zed zed : mapa) {
			instrukce += zed.getX() + ODDELOVAC2 + zed.getY() + ODDELOVAC1;
		}
		return instrukce;
	}

	/**
	 * Vrátí název mapy.
	 *
	 * @return Název mapy.
	 */
	public String getNazev() {
		return nazev;
	}

	/**
	 * Zjistí, zda mapa obsahuje alespoň jednu zeď, nebo je prázdná.
	 *
	 * @return True, pokud je mapa prázdná. False, pokud obsahuje alespoň jednu zeď.
	 */
	public boolean isEmpty() {
		return mapa.isEmpty();
	}

	/**
	 * Přidá do mapy zeď s danými souřadnicemi.
	 * Pokud na daných souřadnicích zeď již existuje, nepřidá se.
	 *
	 * @param x X souřadnice zdi.
	 * @param y Y souřadnice zdi.
	 */
	public void pridejZed(int x, int y) {
		if (!existujeZed(x, y)) {
			mapa.add(new Zed(x, y));
		}
	}

	/**
	 * Smaže zeď na zadaných souřadnicích, pokud existuje.
	 *
	 * @param x Souřadnice zdi X.
	 * @param y Souřadnice zdi Y.
	 * @return True, pokud na daných souřadnicích byla zeď, která se smazala. Jinak false.
	 */
	public boolean smazZed(int x, int y) {
		for (Zed zed : mapa) {
			if (zed.getX() == x && zed.getY() == y) {
				mapa.remove(zed);
				return true;
			}
		}
		return false;
	}

	public void smazZed(GameModel model, Zed zed) {
		zed.odmapuj(model);
		mapa.remove(zed);
	}

	/**
	 * Smaže všechny zdi mapy.
	 */
	public void smazCelouMapu() {
		mapa.clear();
	}

	/**
	 * Zkontroluje, zda daná souřadnice je obsažena jako stěna v mapě.
	 *
	 * @param x Souřadnice x
	 * @param y Souřadnice y
	 * @return true, pokud je obsažena, jinak false.
	 */
	public boolean existujeZed(int x, int y) {
		for (int i = 0; i < mapa.size(); i++) {
			if (x == mapa.get(i).getX() && y == mapa.get(i).getY()) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Vykreslí všechny zdi mapy.
	 *
	 * @param g2d Grafický objekt ke kreslení.
	 */
	public void kresliMapu(Graphics g) {
		for (Zed zed : mapa) {
			zed.draw(g);
		}
	}

	/**
	 * Vykreslí všechny zdi mapy jen černě, bez jejich textur.
	 *
	 * @param g2d Grafický objekt ke kreslení.
	 */
	public void kresliMapuBezTextur(Graphics g) {
		for (Zed zed : mapa) {
			g.setColor(Color.BLACK);
			g.fillRect(zed.getX(), zed.getY(), Consts.SIZE_OF_BOX, Consts.SIZE_OF_BOX);
		}
	}

	/**
	 * Načte mapu ze zadaného InputStreamu.
	 *
	 * @param nazevMapy Název mapy.
	 * @param stream    InputStream, ze kterého se načte mapa.
	 */
	@SuppressWarnings("unchecked")
	public void nactiMapu(String nazevMapy, InputStream stream) {
		ObjectInputStream input = null;
		try {
			input = new ObjectInputStream(stream);
			mapa = (List<Zed>) input.readObject();
			this.nazev = nazevMapy;
		} catch (FileNotFoundException ex) {
			JOptionPane.showMessageDialog(null, "Soubor mapy nebyl nalezen.", "Chyba", JOptionPane.ERROR_MESSAGE);
		} catch (IOException ex) {
			JOptionPane.showMessageDialog(null, "Chyba čtení souboru. Tato mapa nebyla vytvořena v editoru této hry.", "Chyba", JOptionPane.ERROR_MESSAGE);
			System.exit(1);
		} catch (ClassNotFoundException ex) {
			JOptionPane.showMessageDialog(null, "Chyba při čtení souboru mapy.", "Chyba", JOptionPane.ERROR_MESSAGE);
		} finally {
			try {
				input.close();
			} catch (IOException ex) {
				JOptionPane.showMessageDialog(null, "Chyba při zavírání souboru mapy.", "Chyba", JOptionPane.ERROR_MESSAGE);
			}
		}
	}

	/**
	 * Uloží mapu do souboru pod zadaným názvem.
	 *
	 * @param nazevMapy Soubor bude pojmenován podle názvu mapy + koncovka ser.
	 * @return True, pokud se mapu podařilo uložit. Jinak false.
	 */
	public boolean ulozMapu(String nazevMapy) {
		nazevMapy += ".ser";
		if (new File(PATH + nazevMapy).exists()) {
			Object[] options = { "Ano", "Ne" };
			int vyber = JOptionPane.showOptionDialog(null, "Mapa se zadaným názvem již existuje. \n Chcete ji přepsat?", "Chyba", JOptionPane.YES_NO_OPTION, JOptionPane.ERROR_MESSAGE, null, options, options[1]);
			if (vyber != 0) {
				return false;
			}
		}
		ObjectOutputStream output = null;
		try {
			new File(PATH).mkdirs();
			output = new ObjectOutputStream(new FileOutputStream(new File(PATH + nazevMapy)));
			output.writeObject(mapa);
			this.nazev = nazevMapy;
		} catch (FileNotFoundException ex) {
			JOptionPane.showMessageDialog(null, "Ověřte, že máte do oprávnění do složky " + PATH, "Chyba", JOptionPane.ERROR_MESSAGE);
			return false;
		} catch (IOException ex) {
			JOptionPane.showMessageDialog(null, "Ověřte, že máte do oprávnění do složky " + PATH, "Chyba", JOptionPane.ERROR_MESSAGE);
			return false;
		} finally {
			try {
				if (output != null) {
					output.close();
				}
			} catch (IOException ex) {
				JOptionPane.showMessageDialog(null, "Chyba při zavírání souboru mapy.", "Chyba", JOptionPane.ERROR_MESSAGE);
			}
		}
		return true;
	}

	@Override
	public String toString() {
		return nazev;
	}
}
